#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include "canfd_test.h"
#include "pcie_mem.h"

enum pcie_canfd_offset_t {
    PCIE_CANFD_WR_ADDR_OFFSET = 0x0C,       /* 写地址寄存器偏移 */
    PCIE_CANFD_WR_DATA_OFFSET = 0x10,       /* 写数据寄存器偏移 */
    PCIE_CANFD_RD_ADDR_OFFSET = 0x14,       /* 读地址寄存器偏移 */
    PCIE_CANFD_RD_DATA_OFFSET = 0x18,       /* 读数据寄存器偏移 */
};

struct canfd_info_t {
    uint8_t channel;                        /* 通道号 */
    uint8_t channel_min;                    /* 最小通道号 */
    uint8_t channel_max;                    /* 最大通道号 */
    uint32_t baud_rate;                     /* 波特率 */
    uint32_t baud_rate_min;                 /* 最小波特率 */
    uint32_t baud_rate_max;                 /* 最大波特率 */
};

static void write_reg(struct pcie_mem_ctx *ctx, uint32_t reg, uint32_t val)
{
    volatile uint32_t *addr;

    if (!ctx->map_base) {
        printf("错误: 内存未映射\n");
        return;
    }

    /* 验证寄存器偏移量是否在有效范围内 */
    // if (reg >= ctx->map_size) {
    //     printf("错误: 寄存器偏移量0x%x超过了映射大小0x%zx\n",
    //            reg, ctx->map_size);
    //     return;
    // }

    /* 计算地址并执行写入操作 */
    addr = (volatile uint32_t *)((uintptr_t)ctx->map_base);
    *(volatile uint32_t *)((uintptr_t)addr + PCIE_CANFD_WR_ADDR_OFFSET) = reg;
    *(volatile uint32_t *)((uintptr_t)addr + PCIE_CANFD_WR_DATA_OFFSET) = val;

    printf("写入: reg=0x%04X, val=0x%08X\n",
            reg, val);
}

static uint32_t read_reg(struct pcie_mem_ctx *ctx, uint32_t reg)
{
    volatile uint32_t *addr;
    uint32_t val;

    if (!ctx->map_base) {
        printf("错误: 内存未映射\n");
        return 0;
    }

    /* 验证寄存器偏移量是否在有效范围内 */
    // if (reg >= ctx->map_size) {
    //     printf("错误: 寄存器偏移量0x%x超过了映射大小0x%zx\n",
    //            reg, ctx->map_size);
    //     return 0;
    // }

    /* 计算地址并执行读取操作 */
    addr = (volatile uint32_t *)((uintptr_t)ctx->map_base);
    /* 注意：这里需要将字节偏移转换为uint32_t偏移 */
    *(addr + (PCIE_CANFD_RD_ADDR_OFFSET / sizeof(uint32_t))) = reg;
    val = *(addr + (PCIE_CANFD_RD_DATA_OFFSET / sizeof(uint32_t)));

    printf("读取: reg=0x%04X, val=0x%08X\n",
            reg, val);

    return val;
}

void send_canfd(struct pcie_mem_ctx *ctx, uint32_t channel, uint32_t baud_rate)
{
    printf("正在执行寄存器操作序列:\n");

    switch (baud_rate) {
    case 1:
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0018));
        write_reg(ctx, ((channel << 16) | 0x0008), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x000C), 0x000F0F3E);
        write_reg(ctx, ((channel << 16) | 0x0088), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x008C), 0x0007071E);
        write_reg(ctx, ((channel << 16) | 0x0020), 0xFFFFFFFF);
        write_reg(ctx, ((channel << 16) | 0x0A00), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A04), 0x742D0146);
        write_reg(ctx, ((channel << 16) | 0x0A08), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A0C), 0xB4ED0854);
        write_reg(ctx, ((channel << 16) | 0x00E0), 0x00000003);
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000002);
        write_reg(ctx, ((channel << 16) | 0x0100), 0x93E00000);
        write_reg(ctx, ((channel << 16) | 0x0104), 0x6C000000);
        write_reg(ctx, ((channel << 16) | 0x0108), 0x83C15608);
        write_reg(ctx, ((channel << 16) | 0x010C), 0x5750C9D7);
        write_reg(ctx, ((channel << 16) | 0x0090), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0090));
        break;
    case 2:
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0018));
        write_reg(ctx, ((channel << 16) | 0x0008), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x000C), 0x000F0F3E);
        write_reg(ctx, ((channel << 16) | 0x0088), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x008C), 0x0003030E);
        write_reg(ctx, ((channel << 16) | 0x0020), 0xFFFFFFFF);
        write_reg(ctx, ((channel << 16) | 0x0A00), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A04), 0x742D0146);
        write_reg(ctx, ((channel << 16) | 0x0A08), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A0C), 0xB4ED0854);
        write_reg(ctx, ((channel << 16) | 0x00E0), 0x00000003);
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000002);
        write_reg(ctx, ((channel << 16) | 0x0100), 0x93E00000);
        write_reg(ctx, ((channel << 16) | 0x0104), 0x6C000000);
        write_reg(ctx, ((channel << 16) | 0x0108), 0x83C15608);
        write_reg(ctx, ((channel << 16) | 0x010C), 0x5750C9D7);
        write_reg(ctx, ((channel << 16) | 0x0090), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0090));
        break;
    case 4:
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0018));
        write_reg(ctx, ((channel << 16) | 0x0008), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x000C), 0x000F0F3E);
        write_reg(ctx, ((channel << 16) | 0x0088), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x008C), 0x00010106);
        write_reg(ctx, ((channel << 16) | 0x0020), 0xFFFFFFFF);
        write_reg(ctx, ((channel << 16) | 0x0A00), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A04), 0x742D0146);
        write_reg(ctx, ((channel << 16) | 0x0A08), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A0C), 0xB4ED0854);
        write_reg(ctx, ((channel << 16) | 0x00E0), 0x00000003);
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000002);
        write_reg(ctx, ((channel << 16) | 0x0100), 0x93E00000);
        write_reg(ctx, ((channel << 16) | 0x0104), 0x6C000000);
        write_reg(ctx, ((channel << 16) | 0x0108), 0x83C15608);
        write_reg(ctx, ((channel << 16) | 0x010C), 0x5750C9D7);
        write_reg(ctx, ((channel << 16) | 0x0090), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0090));
        break;
    case 5:
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0018));
        write_reg(ctx, ((channel << 16) | 0x0008), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x000C), 0x000F0F3E);
        write_reg(ctx, ((channel << 16) | 0x0088), 0x00000000);
        write_reg(ctx, ((channel << 16) | 0x008C), 0x00010104);
        write_reg(ctx, ((channel << 16) | 0x0020), 0xFFFFFFFF);
        write_reg(ctx, ((channel << 16) | 0x0A00), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A04), 0x742D0146);
        write_reg(ctx, ((channel << 16) | 0x0A08), 0xFFE7FFFE);
        write_reg(ctx, ((channel << 16) | 0x0A0C), 0xB4ED0854);
        write_reg(ctx, ((channel << 16) | 0x00E0), 0x00000003);
        write_reg(ctx, ((channel << 16) | 0x0000), 0x00000002);
        write_reg(ctx, ((channel << 16) | 0x0100), 0x93E00000);
        write_reg(ctx, ((channel << 16) | 0x0104), 0x6C000000);
        write_reg(ctx, ((channel << 16) | 0x0108), 0x83C15608);
        write_reg(ctx, ((channel << 16) | 0x010C), 0x5750C9D7);
        write_reg(ctx, ((channel << 16) | 0x0090), 0x00000001);
        read_reg(ctx, ((channel << 16) | 0x0090));
        break;
    default:
        break;
    }
}

static int usage(int argc, char *argv[])
{
    if (argc != 4) {
        printf("用法: %s <通道> <数据域波特率Mbps>\n", argv[0]);
        printf("示例: %s 0 1\n", argv[0]);
        return EXIT_FAILURE;
    } else {
        return EXIT_SUCCESS;
    }
}

int canfd_test(int argc, char *argv[])
{
    int ret = 0;
    const char *device_address = PCIE_CANFD_DEVICE_ADDRESS;
    uint32_t base_addr = PCIE_CANFD_BASE_ADDR_BAR0;
    size_t map_size = PCIE_CANFD_BAR0_MAP_SIZE;
    struct pcie_mem_ctx ctx = {
        .fd = -1,
        .map_base = NULL,
        .phys_base = 0,
        .map_size = 0
    };
    struct canfd_info_t canfd_info = {
        .channel = 0,
        .channel_min = 0,
        .channel_max = 2,
        .baud_rate = 1,
        .baud_rate_min = 1,
        .baud_rate_max = 5,
    };

    if (EXIT_SUCCESS != usage(argc, argv)) {
        return EXIT_FAILURE;
    }

    canfd_info.channel = atoi(argv[2]);
    if ((canfd_info.channel < canfd_info.channel_min) || 
        (canfd_info.channel > canfd_info.channel_max)) {
        printf("参数错误: %d ≤ channel=%d ≤ %d\n",
               canfd_info.channel_min, canfd_info.channel, canfd_info.channel_max);
        return EXIT_FAILURE;
    }
    canfd_info.baud_rate = atoi(argv[3]);
    if ((canfd_info.baud_rate != 1) && 
        (canfd_info.baud_rate != 2) && 
        (canfd_info.baud_rate != 4) && 
        (canfd_info.baud_rate != 5)) {
        printf("参数错误: baud_rate=%d != 1、2、4、5\n",
               canfd_info.baud_rate);
        return EXIT_FAILURE;
    }

    printf("正在测试通道 %d 的波特率 %dMbps...\n",
           canfd_info.channel, canfd_info.baud_rate);

    if (true == is_pcie_device_enabled(device_address)) {
        printf("设备已启用\n");
    } else {
        printf("设备未启用\n");
        if (enable_pcie_device(device_address) == 0) {
            printf("设备启用成功\n");
        } else {
            printf("设备启用失败!\n");
            return EXIT_FAILURE;
        }
    }

    /* 初始化内存映射 */
    ret = init_pcie_mem(&ctx, base_addr, map_size);
    if (ret < 0) {
        printf("初始化内存映射失败!\n");
        return EXIT_FAILURE;
    }

    /* 执行寄存器访问序列 */
    send_canfd(&ctx, canfd_info.channel, canfd_info.baud_rate);

    /* 清理资源 */
    cleanup_pcie_mem(&ctx);

    printf("通道 %d 的测试已完成\n", canfd_info.channel);
    return EXIT_SUCCESS;
}
